#include <iostream>
#include <chrono>
#include <thread>

#include "boost/date_time/gregorian/gregorian.hpp"

#include "YQLQuotes.hpp"
#include "SQLGateway.hpp"
#include "FieldNameLookup.hpp"

bool testQuote(std::vector<std::string> vecSymbols, bool bStoreInSQL, VerboseOptions verboseOptions) 
{
  
  if (MinVerboseLow(verboseOptions))
  {
      std::cout << " Retreiving Quotes for " << vecSymbols.size() << " symbols.\n";
  }
  
  // YQL's TOS requires a minimum delay of 0.2 seconds between requests. 
  // Add a small delay between each testQuote() to ensure we don't accidently get locked out of the service
  std::this_thread::sleep_for(std::chrono::milliseconds(25));
  
   FieldNameLookup lookup;
   
   if (vecSymbols.empty()) { return false; }
  
   YQLQuotes yql;
   
   for (unsigned vecIndex = 0; vecIndex < vecSymbols.size(); ++vecIndex)
   {
      yql.addSymbol(vecSymbols[vecIndex]);  
   }
   
   
   if (!yql.loadFromYQL()) { 
     std::cout << "testQuote() - failed loadFromYQL()\n";
     return false; 
   }
   
   if (MinVerboseDebug(verboseOptions)) {
    std::cout << "   " << yql.getURL() << "\n";
   }
   
   if (!yql.parseXML()) {
     std::cout << "testQuote() - failed parseXML()\n";     
     return false;
   }

   if (yql.getNumQuotes() == 0) {
      std::cout << "testQuote() - getNumQuotes() is 0\n";
      return false;
   }
   
  if (MinVerboseHigh(verboseOptions)) {
   std::cout << "testQuote() got " << yql.getNumQuotes() << " quotes.\n";
  }

  if (MinVerboseDebug(verboseOptions)) {
     // Only display output on verbose
   for (int quoteIndex = 0; (unsigned)quoteIndex < yql.getNumQuotes(); quoteIndex++)
   {
      ResultQuote rq;
      if (!yql.getQuote(quoteIndex, rq)) {
         std::cout << "testQuote() - yql.getQuote() failure\n";
	 return false;
      }
      
      std::cout << "Quote for " << rq.getSymbol() << "\n";
      VectorFieldName vecFieldNames = rq.getAllFieldNames();
      
      for (unsigned int pairIndex = 0; pairIndex < vecFieldNames.size(); pairIndex++)
      {
	 
         FieldName fieldName = vecFieldNames[pairIndex];
	 
	 
         std::string   strFieldName;
	 std::string   strDataValue;

	 if ( !(rq.lookupFieldNameString(fieldName, strFieldName)) ) { std::cout << "rq.lookup failure!\n"; return false; }
	 if ( !(rq.lookupDataString(fieldName, strDataValue, TrimOptions::None)) ) { std::cout << "rq.lookup failure!\n"; return false; }
	 
	 std::cout << "   " << strFieldName << " : " << strDataValue << "\n";
         
      }
   } // for (int quoteIndex
  } // MinVerboseDebug()
  
   
   if (bStoreInSQL) {
      SQLGateway sql;
      
      if (!sql.Connect()) {
         std::cout << "testQuote() - Error connecting to SQL server!\n";
	 return false;
      }
      
      for (int quoteIndex = 0; (unsigned)quoteIndex < yql.getNumQuotes(); quoteIndex++)
      {
         ResultQuote rq;
	 if (!yql.getQuote(quoteIndex, rq)) {
            std::cout << "testQuote() - yql.getQuote() failure\n";
	    return false;
         }
      
	 if (MinVerboseDebug(verboseOptions)) {
	  std::cout << "  SQL Insert for: " << rq.getSymbol() << "\n";
	 }

	 SetString setNotInTable;
	 
	 if (!sql.insertResultQuote("tableName", rq, setNotInTable))
	 {
	    std::cout << "testQuote() - sql.insertFieldValuePairCollection() failure!\n"
	              << sql.getLastSQLError() << "\n"
		      << sql.getLastSQLQuery() << "\n\n";
            return false;		     
	 } else {
	    if (MinVerboseDebug(verboseOptions)) {
	      std::cout << sql.getLastSQLQuery() << "\n";  
	    }
	 }
	 
	 if (!setNotInTable.empty()) 
	 {
	    std::cout << "   Missing From Table: ";
	    for (SetString::const_iterator itr = setNotInTable.begin(); itr != setNotInTable.end(); ++itr)
	    {
	       if (itr != setNotInTable.begin()) { std::cout << ", "; }
	       std::cout << *itr;
	    }
	 }
	 
	 if (MinVerboseDebug(verboseOptions)) {
	  std::cout << "Inserted rows into SQL.\n";
	 }
	 
      } // for (int QuoteIndex)
      
      if (MinVerboseHigh(verboseOptions)) {
	std::cout << "  Inserted " << yql.getNumQuotes() << " quotes into database." << "\n";
      }
      
      if (!sql.transactionCommit()) {
	std::cout << "Error on SQL commit!\n";
      }
   } // bStoreInSQL
   
   
   
   return true;
   
}